package disorderly;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;

import com.google.gson.Gson;

public class Main {
  private static final String GROUP_CALENDAR_FILE = "C:\\Games\\World of Warcraft\\WTF\\Account\\ROMETPIHO\\SavedVariables\\GroupCalendar5.lua";
  private static final String ATTENDANCE_FILE = "D:\\Projects\\disorderly\\disorderly_attendance.txt";
  private static final String ATTENDANCE_HTML = "D:\\Projects\\disorderly\\disorderly_attendance.html";
  private static final String ATTENDANCE_FILE_BACKUP = "D:\\Projects\\disorderly\\disorderly_attendance.backup";
  private static final String ENCODING = "UTF-8";

  public static Set<Event> getGroupCalendarAttendance(GroupCalendar groupCalendar) {
    Set<Event> events = new HashSet<Event>();
    for (Events es : groupCalendar.Realms.QuelThalas.Characters.Jumal0x060000000689462A.Events) {
      for (Event e : es.events) {
        if (e.Attendance != null && (e.Title.equals("Firelands") || e.Title.equals("Dragon Soul") || e.Title.equals("Mogu'shan Vaults") || e.Title.equals("Throne of Thunder (10 Player)") || e.Title.equals("Siege of Orgrimmar (10 Player)")) || e.Title.equals("Highmaul")) {
          events.add(e);
        }
      }
    }
    return events;
  }

  public static Set<Event> getSavedAttendance() throws IOException {
    File file = new File(ATTENDANCE_FILE);
    if (!file.exists()) {
      return new HashSet<Event>();
    }
    InputStreamReader stream = new InputStreamReader((new FileInputStream(file)), ENCODING);
    Gson gson = new Gson();
    GroupCalendar groupCalendar = gson.fromJson(stream, GroupCalendar.class);
    Set<Event> events = new HashSet<Event>();
    for (Events es : groupCalendar.Realms.Characters.Events) {
      for (Event e : es.events) {
        events.add(e);
      }
    }
    stream.close();
    File backup = new File(ATTENDANCE_FILE_BACKUP);
    if (backup.exists()) {
      backup.delete();
    }
    file.renameTo(new File(ATTENDANCE_FILE_BACKUP));
    return events;
  }

  private static void saveAttendance(List<Event> l) throws IOException {
    GroupCalendar groupCalendar = new GroupCalendar();
    groupCalendar.Realms = new Realm();
    groupCalendar.Realms.Characters = new Characters();
    Events[] events = new Events[1];
    events[0] = new Events();
    groupCalendar.Realms.Characters.Events = events;
    groupCalendar.Realms.Characters.Events[0].events = l.toArray(new Event[l.size()]);
    Gson gson = new Gson();
    OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(ATTENDANCE_FILE), ENCODING);
    gson.toJson(groupCalendar, GroupCalendar.class, writer);
    writer.flush();
    writer.close();
  }

  private static void generateHtml(List<Event> l) throws IOException {
    Set<Attendance> set = new HashSet<Attendance>();
    for (Event event : l) {
      for (Attendance a : event.Attendance) {
        set.add(a);
      }
    }
    List<Attendance> ppl = new ArrayList<Attendance>();
    for (Attendance a : set) {
      ppl.add(a.clone());
    }
    Map<Attendance, Integer> pastEvents = new HashMap<Attendance, Integer>();
    Map<Attendance, Integer> pastEvents2w = new HashMap<Attendance, Integer>();
    Map<Attendance, Integer> pastEvents2m = new HashMap<Attendance, Integer>();
    Calendar cal2w = Calendar.getInstance();
    cal2w.add(Calendar.DATE, -14);
    Date date2w = cal2w.getTime();
    Calendar cal2m = Calendar.getInstance();
    cal2m.add(Calendar.MONTH, -1);
    Date date2m = cal2m.getTime();

    boolean first = true;
    if (!l.isEmpty()) {
      for (Event e : l) {
        boolean past = e.getCalDate().getTime().before(new Date());
        boolean last2w = date2w.before(e.getCalDate().getTime());
        boolean last2m = date2m.before(e.getCalDate().getTime());
        for (Attendance a : ppl) {
          Attendance at = null;
          if (ArrayUtils.contains(e.Attendance, a)) {
            if (past) {
              Integer attended = pastEvents.get(a);
              if (attended == null) {
                attended = 1;
              }
              else {
                attended++;
              }
              at = e.Attendance[ArrayUtils.lastIndexOf(e.Attendance, a)];
              Integer attendanceValue = Status.fromAttendance(at).getAttendanceValue();
              if (a.Attendance > 0 || attendanceValue > 0) {
                pastEvents.put(a, attended);
              }

              // 2 weeks attendance
              if (last2w) {
                attended = pastEvents2w.get(a);
                if (attended == null) {
                  attended = 1;
                }
                else {
                  attended++;
                }
                pastEvents2w.put(a, attended);
              }
              // 2 month attendance
              if (last2m) {
                attended = pastEvents2m.get(a);
                if (attended == null) {
                  attended = 1;
                }
                else {
                  attended++;
                }
                pastEvents2m.put(a, attended);
              }
            }
          }
          else if (first) {
            a.InviteStatus = 0;
          }
          Integer attendanceValue = Status.fromAttendance(at).getAttendanceValue();
          a.Attendance = a.Attendance + attendanceValue;
          if (last2w) {
            a.Attendance2w = a.Attendance2w + attendanceValue;
          }
          if (last2m) {
            a.Attendance2m = a.Attendance2m + attendanceValue;
          }
        }
        first = false;
      }
      for (Attendance attendance : ppl) {
        attendance.pastEvents = pastEvents;
        attendance.pastEvents2w = pastEvents2w;
        attendance.pastEvents2m = pastEvents2m;
      }
    }
    Collections.sort(ppl);
    StringBuilder sb = new StringBuilder();
    sb.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n");
    sb.append("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n");
    sb.append("<head>\n");
    sb.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n");
    sb.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />\n");
    sb.append("<title>Disorderly attendance</title\n");
    sb.append("</head>\n");
    sb.append("<body>\n");
    sb.append("<div>\n");
    sb.append("<table>\n");
    sb.append("<tbody>\n");
    sb.append("<tr>\n");
    sb.append("<td>Name</td>\n");
    sb.append("<td title=\"2 week's attendance\" class=\"title\">&nbsp;2 week&nbsp;</td>\n");
    sb.append("<td title=\"1 month attendance\"class=\"title\">&nbsp;1 month&nbsp;</td>\n");
    sb.append("<td title=\"Total attendance\"class=\"title\">&nbsp;Total&nbsp;</td>\n");
    sb.append("<td title=\"Total raid count\"class=\"title\">&nbsp;Count&nbsp;</td>\n");
    boolean reset = false;
    int resetNr = 1;
    for (Event event : l) {
      String cl = "title";
      if (reset) {
        cl = "title reset";
      }
      sb.append("<td class=\"" + cl + "\" title=\"" + event.toString() + "\">\n");
      sb.append(event.getDate() + "\n");
      sb.append("</td>\n");
      if (event.isReset()) {
        reset = !reset;
        resetNr++;
      }
      if (resetNr % 9 == 0) {
        sb.append("<td>Name</td>\n");
        resetNr++;
      }
    }
    sb.append("</tr>\n");
    for (Attendance a : ppl) {
      if (a.Name.equals("Akali")) {
        continue;
      }
      first = true;
      sb.append("<tr>\n");
      int nr = 1;
      for (Event event : l) {
        List<Attendance> list = Arrays.asList(event.Attendance);
        if (first) {
          first = false;
          sb.append("<td class=\"" + a.ClassName + "\">\n");
          sb.append(a.Name + "\n");
          sb.append("</td>\n");
          sb.append("<td>\n");
          sb.append("&nbsp;" + a.getAttendancePercentage2w() + "%&nbsp;\n");
          sb.append("</td>\n");
          sb.append("<td>\n");
          sb.append("&nbsp;" + a.getAttendancePercentage2m() + "%&nbsp;\n");
          sb.append("</td>\n");
          sb.append("<td>\n");
          sb.append("&nbsp;" + a.getAttendancePercentage() + "%&nbsp;\n");
          sb.append("</td>\n");
          sb.append("<td>\n");
          sb.append("&nbsp;" + a.getPastRaidedCount() + "&nbsp;\n");
          sb.append("</td>\n");
        }
        Attendance at = list.contains(a) ? list.get(list.indexOf(a)) : null;
        Status status = Status.fromAttendance(at);
        sb.append("<td class=\"title status" + status.getStatus() + "\" title=\"" + status.name().toLowerCase().replace("_", " ") + "\">\n");
        sb.append(status.getName() + "\n");
        sb.append("</td>\n");
        if (event.isReset()) {
          nr++;
        }
        if (nr % 9 == 0) {
          sb.append("<td class=\"" + a.ClassName + "\">\n");
          sb.append(a.Name + "\n");
          sb.append("</td>\n");
          nr++;
        }
      }
      sb.append("</tr>\n");
    }
    sb.append("</tbody>\n");
    sb.append("</table>\n");
    sb.append("</div>\n");
    sb.append("<p />\n");
    sb.append("<div class=\"legend\">\n");
    sb.append("<table>\n");
    sb.append("<tbody>\n");
    for (Status s : Status.values()) {
      if (s.getName().isEmpty()) {
        continue;
      }
      sb.append("<tr>\n");
      sb.append("<td class=\"status" + s.getStatus() + "\">\n");
      sb.append(s.getName() + "\n");
      sb.append("</td>\n");
      sb.append("<td>\n");
      sb.append(s.name().toLowerCase().replace("_", " ") + "\n");
      sb.append("</td>\n");
      sb.append("</tr>\n");
    }
    sb.append("</tbody>\n");
    sb.append("</table>\n");
    sb.append("</div>\n");
    sb.append("</body>\n");
    sb.append("</html>\n");
    sb.append("\n");
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(ATTENDANCE_HTML), ENCODING));
    writer.write(sb.toString());
    writer.flush();
    writer.close();
  }

  public static GroupCalendar readGroupCalendar() throws IOException {
    StringBuilder luaFile = new StringBuilder("{");
    boolean ranks = false;
    boolean events = false;
    boolean eventStart = false;
    boolean attendance = false;
    boolean attendanceStart = false;
    boolean descriptionTags = false;
    boolean group = false;
    boolean groupStart = false;
    int eventTemplates = 0;
    String l = "";
    String line = "";

    BufferedReader reader = new BufferedReader(new InputStreamReader((new FileInputStream(GROUP_CALENDAR_FILE)), ENCODING));
    line = reader.readLine();
    line = reader.readLine();

    while (!(line = reader.readLine()).startsWith("GroupCalendarClock_Data")) {
      l = line.replace("=", ":").replace("[", "").replace("]", "").trim();
      if (l.contains("Quel'Thalas")) {
        l = l.replace("'", "");
      }
      if (l.contains("Player-1396-0689462A")) {
        l = l.replace("Player-1396-0689462A", "Jumal0x060000000689462A");
      }
      if (l.contains("EventTemplates")) {
        luaFile.append(l);
        eventTemplates++;
      }
      else if (eventTemplates > 0) {
        if (l.contains("},")) {
          eventTemplates--;
        }
        if (l.contains("{")) {
          eventTemplates++;
        }
      }
      if (eventTemplates != 0) {
        continue;
      }
      // DescriptionTags
      if (l.contains("DescriptionTags")) {
        descriptionTags = true;
      }
      if (descriptionTags && l.equals("},")) {
        descriptionTags = false;
      }
      else if (!descriptionTags) {
        // Group
        if (l.contains("\"Group\"")) {
          group = true;
          l = l.replace("{", "[");
        }
        else if (group) {
          if (!groupStart && l.contains("{")) {
            groupStart = true;
            l = "{";
          }
          if (l.equals("},")) {
            if (groupStart) {
              groupStart = false;
            }
            else {
              l = "],";
              group = false;
            }
          }
        }
        // Attendance
        if (l.contains("Attendance")) {
          attendance = true;
          l = l.replace("{", "[");
        }
        else if (attendance) {
          if (!attendanceStart && l.contains("{")) {
            attendanceStart = true;
            l = "{";
          }
          if (l.equals("},")) {
            if (attendanceStart) {
              attendanceStart = false;
            }
            else {
              l = "],";
              attendance = false;
            }
          }
        }
        if (!attendance && !group) {
          // Events
          if (l.contains("Events")) {
            events = true;
            l = l.replace("{", "[");
          }
          if (events && !eventStart && l.contains("{") && !l.equals("{")) {
            l = "{";
          }
          else if (events && !eventStart && l.equals("{")) {
            eventStart = true;
            l = "\"events\":[{";
          }
          if (events && l.equals("},")) {
            if (eventStart) {
              eventStart = false;
              l = "]},";
            }
            else {
              l = "],";
              events = false;
            }
          }
        }
      }

      // --
      if (l.contains("--")) {
        l = l.split("--")[0].trim();
      }

      // Ranks
      if (l.contains("Ranks")) {
        l = l.replace("{", "[");
        ranks = true;
      }
      if (ranks && l.contains("}")) {
        l = l.replace("}", "]");
        ranks = false;
      }

      if (luaFile.charAt(luaFile.length() - 1) == ',' && (l.startsWith("}") || l.startsWith("]"))) {
        luaFile.deleteCharAt(luaFile.length() - 1);
      }
      luaFile.append(l);
    }
    Gson gson = new Gson();
    GroupCalendar groupCalendar = gson.fromJson(luaFile.toString(), GroupCalendar.class);
    reader.close();
    return groupCalendar;
  }

  public static void main(String[] args) {
    try {
      GroupCalendar groupCalendar = readGroupCalendar();
      Set<Event> attendance = getGroupCalendarAttendance(groupCalendar);
      attendance.addAll(getSavedAttendance());
      for (Event event : attendance) {
        List<Attendance> list = event.getSortedAttendance();
        event.Attendance = list.toArray(new Attendance[list.size()]);
      }
      List<Event> l = new ArrayList<Event>(attendance);
      Collections.sort(l);
      Collections.reverse(l);
      saveAttendance(l);
      generateHtml(l);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}
